﻿/********************************************************************************
* Copyright 2010 Zane Thorn (zane.thorn@gmail.com)                              *
*                                                                               *
* NeturalMath is free software: you can redistribute it and/or modify           *
* it under the terms of the GNU Lesser General Public License as published by   *
* the Free Software Foundation, either version 3 of the License, or             *
* (at your option) any later version.                                           *
*                                                                               *
* NeturalMath is distributed in the hope that it will be useful,                *
* but WITHOUT ANY WARRANTY; without even the implied warranty of                *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                 *
* GNU Lesser General Public License for more details.                           *
*                                                                               *
* You should have received a copy of the GNU Lesser General Public License      *
* along with NeturalMath.  If not, see <http://www.gnu.org/licenses/>.          *
********************************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NeturalMath.Expressions
{
    public class AssignmentExpression:VoidExpression
    {
        #region Constructors

        public AssignmentExpression(LookupExpression identifier,ValueExpression assignment,MathRuntime runtime)
            :base(runtime)
        {
            Target = identifier;
            Assignment = assignment;
        }

        #endregion

        #region Public Properties

        public LookupExpression Target { get; private set; }

        public ValueExpression Assignment { get; private set; }

        #endregion

        #region Method Overrides

        protected override void PerformActions()
        {
            var scope = Scope.Private;
            var target = Target;

            var accessModifier = target as AccessModifierExpression;
            while (accessModifier != null)
            {
                scope = accessModifier.Scope;
                target = accessModifier.Lookup;
                accessModifier = target as AccessModifierExpression;
            }

            var function = target as FunctionUseExpression;
            var domain = Assignment as DomainExpression;
            var lookup = Assignment as LookupExpression;

            if (function != null)
            {
                var parameters = GetParameters(function);
                Runtime.CurrentDomain.CreateFunction(target.MemberName, Assignment,parameters , scope);
            }
            else if (domain!=null)
            {
                var d = Runtime.CurrentDomain.CreateDomain(target.MemberName, domain, scope);
                d.Invoke();
            }
            else if (lookup!=null)
            {
                var symbol = Runtime.CurrentDomain.GetSymbol(lookup.MemberName);
                if (symbol == null)
                    throw new MathException(ErrorCodes.ExpectedIndentifier,"Identifier not found");

                var d = symbol as Domain;
                var f = symbol as Function;
                var v = symbol as Variable;

                if (f != null)
                    Runtime.CurrentDomain.CloneFunction(target.MemberName, f, scope);
                else if (d != null)
                    Runtime.CurrentDomain.CloneDomain(target.MemberName, d, scope);
                else if (v != null)
                    Runtime.CurrentDomain.CloneVariable(target.MemberName, v, scope);
                else
                    throw new MathException(ErrorCodes.ExpectedIndentifier, "Identifier not found");
            }
            else
            {
                if (scope.Equals(Scope.Private))
                    Runtime.CurrentDomain.SafeSetVariable(target.MemberName, Assignment);
                else
                    Runtime.CurrentDomain.CreateVariable(target.MemberName, Assignment, scope);
            }
        }

        private IEnumerable<FunctionParameterExpression> GetParameters(FunctionUseExpression function)
        {
            foreach (var p in function.Parameters)
            {

                var assignment = p as AssignmentExpression;
                if (assignment != null)
                {
                    yield return new FunctionParameterExpression(assignment.Target.MemberName, assignment.Assignment,Runtime);
                    continue;
                }

                var lookup = p as LookupExpression;
                if (lookup != null)
                {
                    yield return new FunctionParameterExpression(lookup.MemberName,Runtime);
                    continue;
                }

                throw new MathException(ErrorCodes.InvalidParameterDefinition, "Invalid parameter definition");
            }
        }

        #endregion
    }
}
